package com.tongfusoft.document.doc.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import com.tongfuosft.document.system.service.IDocSystempParamService;
import com.tongfusoft.da.pagehelper.PageInfo;
import com.tongfusoft.da.template.service.impl.DataTemplateServiceImpl;
import com.tongfusoft.document.doc.service.IPluploadService;
import com.tongfusoft.document.entity.BigFiles;
import com.tongfusoft.document.entity.FileFragments;
import com.tongfusoft.document.entity.UploadInfo;
import com.tongfusoft.document.entity.Users;
import com.tongfusoft.exception.ServiceException;
import com.tongfusoft.utils.DateUtils;
import com.tongfusoft.utils.UIDGeneratorService;

@Service("pluploadService")
public class PluploadServiceImpl extends DataTemplateServiceImpl implements IPluploadService {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	private static String syn = "";
	@Autowired
	@Qualifier("docSystemParamService")
	public IDocSystempParamService systemParamService;

	@Override
	public boolean saveFile(MultipartFile file, Users user, UploadInfo uploadInfo) {
		String uploadDirectory = this.systemParamService.getParamByCode("big.file.path").getSystemValue()
				+ File.separator + DateUtils.format(new Date(), "yyyyMMdd")+ File.separator + "tmp" + File.separator + uploadInfo.getMd5value()+ File.separator;
		FileOutputStream outStream = null;
		try {
			byte[] data = readInputStream(file.getInputStream());
			String filePath = uploadDirectory + UIDGeneratorService.getUUID() + "." + uploadInfo.getExt();
			// new一个文件对象用来保存图片，默认保存当前工程根目录
			File uploadFile = new File(filePath);
			// 判断文件夹是否存在，不存在就创建一个
			File fileDirectory = new File(uploadDirectory);
			uploadInfo.setPath(filePath);
			synchronized (syn) {
				if (!fileDirectory.exists()) {
					if (!fileDirectory.mkdirs()) {
						throw new ServiceException("保存文件的父文件夹创建失败！路径为：" + uploadDirectory);
					}
				}
			}

			// 创建输出流
			outStream = new FileOutputStream(uploadFile);
			outStream.write(data);
			outStream.flush();
			if (StringUtils.isEmpty(uploadInfo.getChunks()) && StringUtils.isEmpty(uploadInfo.getChunk())) {
				String copyFilePath = this.systemParamService.getParamByCode("big.file.path").getSystemValue()
						+ File.separator + DateUtils.format(new Date(), "yyyyMMdd") + File.separator
						+ UIDGeneratorService.getUUID() + uploadInfo.getExt();
				File copyFile = new File(copyFilePath);
				FileCopyUtils.copy(uploadFile, copyFile);
				BigFiles files = new BigFiles();
				files.setFilePath(filePath);
				files.setMd5(uploadInfo.getMd5value());
				files.setFileName(uploadInfo.getFileName());
				files.setStatus("1");
				files.setFileSize(String.valueOf(new File(filePath).length()));
				logger.debug("big file size:{}", new Long(new File(filePath).length()).intValue());
				this.addFileInfo(files, user);
			}
			return uploadFile.exists();
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServiceException(e);
		} finally {

			try {
				outStream.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	public void Uploaded(UploadInfo uploadInfo, Users user) {
		if (isFragmentsExist(uploadInfo.getMd5value(), uploadInfo.getChunk())) {
			this.addFragments(uploadInfo, user);
		}

		boolean allUploaded = isAllUploaded(uploadInfo.getMd5value(), uploadInfo.getChunks(),user);
		int chunksNumber = Integer.parseInt(uploadInfo.getChunks());

		if (allUploaded) {
			String filePath = mergeFile(uploadInfo, user);
		}
	}

	// 保存碎片
	private void addFragments(UploadInfo uploadInfo, Users user) {

		FileFragments ff = new FileFragments();
		ff.setChunk(Integer.parseInt(uploadInfo.getChunk()));
		ff.setChunks(Integer.parseInt(uploadInfo.getChunks()));
		ff.setMd5(uploadInfo.getMd5value());
		ff.setFileName(uploadInfo.getFileName());
		ff.setExt(uploadInfo.getExt());
		ff.setPath(uploadInfo.getPath());
		ff.setCreatedBy(user.getUserId());
		ff.setLastUpdBy(user.getUserId());
		ff.setGuid(uploadInfo.getGuid());
		ff.setCreatedDate(new Timestamp(new Date().getTime()));
		ff.setLastUpdDate(new Timestamp(new Date().getTime()));
		ff.setId(UIDGeneratorService.getUUID());
		super.insert("addBigFileFragments", ff);
	}

	private void addFileInfo(BigFiles files, Users user) {
		files.setCreatedBy(user.getUserId());
		files.setLastUpdBy(user.getUserId());
		files.setCreatedDate(new Timestamp(new Date().getTime()));
		files.setLastUpdDate(new Timestamp(new Date().getTime()));
		files.setId(UIDGeneratorService.getUUID());

		files.setStatus("1");
		super.insert("saveBigFile", files);
	}

	private byte[] readInputStream(InputStream inStream) throws Exception {
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		// 创建一个Buffer字符串
		byte[] buffer = new byte[1024];
		// 每次读取的字符串长度，如果为-1，代表全部读取完毕
		int len;
		// 使用一个输入流从buffer里把数据读取出来
		while ((len = inStream.read(buffer)) != -1) {
			// 用输出流往buffer里写入数据，中间参数代表从哪个位置开始读，len代表读取的长度
			outStream.write(buffer, 0, len);
		}
		// 关闭输入流
		inStream.close();
		// 把outStream里的数据写入内存
		return outStream.toByteArray();
	}

	private boolean isAllUploaded(String md5, String chunks,Users user) {

	
		int size =this.fragmentListByMd5(md5,user).size();

		if (size > Integer.parseInt(chunks)) {
			FileFragments ff = new FileFragments();
			ff.setMd5(md5);
			super.delete("deleteFile_fragments", ff);
			throw new ServiceException("file fregemnts error");
		}
		return (size == Integer.parseInt(chunks));
	}

	/**
	 * @param chunksNumber
	 * @param ext
	 * @param guid
	 * @param uploadFolderPath
	 * @throws Exception
	 */
	/**
	 * @param uploadInfo
	 * @param user
	 * @return
	 */
	private synchronized String mergeFile(UploadInfo uploadInfo, Users user) {
		BigFiles bigFile = new BigFiles();
		bigFile.setMd5(uploadInfo.getMd5value());
		bigFile = (BigFiles) super.selectOne("selectBigFileList", bigFile);
		if (bigFile != null) {

			return bigFile.getFilePath();
		}

		try {
			String filePath = this.systemParamService.getParamByCode("big.file.path").getSystemValue() + File.separator
					+ DateUtils.format(new Date(), "yyyyMMdd") + File.separator + uploadInfo.getMd5value()
					+ File.separator;
			
			
			
			// 判断文件夹是否存在，不存在就创建一个
			File fileDirectory = new File(filePath);
			uploadInfo.setPath(filePath);
			synchronized (syn) {
				if (!fileDirectory.exists()) {
					if (!fileDirectory.mkdirs()) {
						throw new ServiceException("保存文件的父文件夹创建失败！路径为：" + fileDirectory);
					}
				}
			}
			
			filePath=filePath + UIDGeneratorService.getUUID() + "." + uploadInfo.getExt();
			
			
			SequenceInputStream s;
			FileFragments ff = new FileFragments();
			ff.setMd5(uploadInfo.getMd5value());
			
			List<FileFragments> fileList =this.fragmentListByMd5(ff.getMd5(),user);
			InputStream s1 = new FileInputStream(fileList.get(0).getPath());
			InputStream s2 = new FileInputStream(fileList.get(1).getPath());
			s = new SequenceInputStream(s1, s2);
			for (int i = 2; i < fileList.size(); i++) {
				logger.debug("File Fregement Path {}", fileList.get(i).getPath());
				InputStream s3 = new FileInputStream(fileList.get(i).getPath());
				s = new SequenceInputStream(s, s3);
			}

			// 通过输出流向文件写入数据
			saveStreamToFile(s, filePath);

			// 合并完成后写入表中
			BigFiles files = new BigFiles();
			files.setFilePath(filePath);
			files.setMd5(uploadInfo.getMd5value());
			files.setFileName(uploadInfo.getFileName());
			files.setStatus("1");
			files.setFileSize(String.valueOf(new File(filePath).length()));
			this.addFileInfo(files, user);

			// 删除保存分块文件的文件夹
			for (int i = 0; i < fileList.size(); i++) {

				File file = new File(fileList.get(i).getPath());
				file.delete();

			}

			ff.setMd5(uploadInfo.getMd5value());
			ff.setCreatedBy(user.getUserId());
			super.delete("deleteFile_fragments", ff);
			return filePath;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage());
			throw new ServiceException(e);
		}

	}

	/**
	 * 从stream中保存文件
	 *
	 * @param inputStream
	 *            inputStream
	 * @param filePath
	 *            保存路径
	 * @throws Exception
	 *             异常 抛异常代表失败了
	 */
	private void saveStreamToFile(InputStream inputStream, String filePath) throws Exception {
		/* 创建输出流，写入数据，合并分块 */
		OutputStream outputStream = new FileOutputStream(filePath);
		byte[] buffer = new byte[1024];
		int len = 0;
		try {
			while ((len = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, len);
				outputStream.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			outputStream.close();
			inputStream.close();
		}
	}

	@Override
	public boolean isMd5Exist(String fileMd5,Users user) {
		// TODO Auto-generated method stub

		BigFiles bigFile = new BigFiles();
		bigFile.setMd5(fileMd5);
		bigFile.setCreatedBy(user.getUserId());
		bigFile = (BigFiles) super.selectOne("selectBigFileList", bigFile);
		if (bigFile == null) {

			return false;
		} else {
			return true;
		}

	}

	@Override
	public PageInfo bigFilesList(int pageSize, int pageIndex, BigFiles bigFile) {
		return super.findPage("selectBigFileList", bigFile, pageSize, pageIndex);
	}

	@Override
	public boolean isFragmentsExist(String md5, String chunk) {
		FileFragments ff = new FileFragments();
		ff.setMd5(md5);
		ff.setChunk(Integer.parseInt(chunk));
		if (super.selectList("selectfragments", ff).size() == 0) {

			return true;
		} else {
			return false;
		}
	}

	@Override
	public void deleteBigFile(String id,Users user) {

		BigFiles bigFile = new BigFiles();
		bigFile.setId(id);
	
		bigFile.setCreatedBy(user.getUserId());
		bigFile = (BigFiles)super.selectOne("selectBigFileList", bigFile);
		File dFile = new File(bigFile.getFilePath());
		if(dFile.exists()){
			dFile.delete();
		}
		
		bigFile.setLastUpdBy(user.getUserId());
		bigFile.setLastUpdDate(new Timestamp(new Date().getTime()));
		super.update("deleteBigFiles", bigFile);
	}

	@Override
	public List<FileFragments> fragmentListByMd5(String md5,Users user) {

		FileFragments ff = new FileFragments();
		ff.setMd5(md5);
		ff.setCreatedBy(user.getUserId());
		return super.selectList("selectfragmentsByMd5", ff);
	}
}
